package top.huxy.boot.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import top.huxy.boot.BlogApiApplication;
import top.huxy.boot.config.RedisKeyConfig;
import top.huxy.boot.exception.RequestParamIllegalException;
import top.huxy.boot.mapper.VisitLogMapper;
import top.huxy.boot.model.dto.Pager;
import top.huxy.boot.model.dto.Query;
import top.huxy.boot.model.entity.VisitLog;
import top.huxy.boot.model.vo.VOVisitorCityInfo;
import top.huxy.boot.service.VisitLogService;
import top.huxy.boot.util.DateUtils;
import top.huxy.boot.util.ExceptionUtils;
import top.huxy.boot.util.StringUtils;
import top.huxy.boot.util.obj.Pagination;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Classname VisitLogServiceImpl
 * @Description VisitLogServiceImpl
 * @Date 2021/9/17 9:24
 * @Created by huxy
 */
@Service
@Slf4j
public class VisitLogServiceImpl implements VisitLogService {

    public static VisitLogServiceImpl getInstance() {
        return BlogApiApplication.app.getBean(VisitLogServiceImpl.class);
    }

    @Autowired
    RedisTemplate<String, Object> redisTemplate;

    @Autowired
    VisitLogMapper visitLogMapper;

    @Override
    @Deprecated
    public List<VisitLog> getVisitLogList(Pagination pagination) {
        return visitLogMapper.getVisitLogList(pagination.getLeftIndex(), pagination.getRightIndex());
    }

    @Override
    public PageInfo<VisitLog> getVisitLogList(Pager pager, Query query) {
        query
                .allowKey("startTime", false)
                .replaceValue("startTime", Long::parseLong)

                .allowKey("endTime", false)
                .replaceValue("endTime", Long::parseLong)

                .allowKey("requestId", false)
                .allowKey("method", false)
                .allowKey("ip", false)
                .allowKey("state", false)
        ;
        PageHelper.startPage(pager.getPage(), pager.getPageSize(), "create_time desc");
        return new PageInfo<>(visitLogMapper.getVisitLogListByFilter(query.getQueryMap()));
    }

    @Override
    public int getVisitLogCount() {
        return visitLogMapper.getVisitLogCount();
    }

    public String saveVisitLog() {
        //将redis中的访客数据持久化到数据库
        Long size = redisTemplate.opsForList().size(RedisKeyConfig.TEMP_VISITOR_LOG);
        if (size == null || size == 0) {
            return "未保存任何数据，因为size=" + size;
        }

        long timeStart = System.currentTimeMillis();

        StringBuilder sb = new StringBuilder();
        long errCount = 0;

        for (int i = 0; i < size; i++) {
            Object o = redisTemplate.opsForList().leftPop(RedisKeyConfig.TEMP_VISITOR_LOG);
            if (o instanceof VisitLog) {
                VisitLog visitLog = (VisitLog) o;
                try {
                    visitLogMapper.addLog(visitLog);
                } catch (Exception e) {
                    ExceptionUtils.logError(new RuntimeException("数据插入失败,o=" + o.toString() + ",e=" + StringUtils.getStackTrace(e)));
                    errCount++;
                }
            } else {
                String msg = "不是预期的值,(o instanceof VisitLog) is false";
                ExceptionUtils.logError(new RuntimeException(msg));
                sb.append(msg).append("\n");
                errCount++;
            }

            //锁续命
            redisTemplate.expire(RedisKeyConfig.LOCK_SAVE_VISITOR_LOG, 30L, TimeUnit.SECONDS);
        }

        long timeEnd = System.currentTimeMillis();
        sb.append("执行消耗")
                .append(timeEnd - timeStart)
                .append("毫秒。")
                .append("错误次数")
                .append(errCount)
                .append("。执行总数")
                .append(size);
        return sb.toString();
    }

    @Override
    public VisitLog getVisitLogByRequestId(String requestId) {
        if (StringUtils.isEmpty(requestId)) throw new RequestParamIllegalException("requestId must be not empty");
        return visitLogMapper.getVisitLogByRequestId(requestId);
    }

    @Override
    public void saveVisitLog2Redis(VisitLog visitLog) {
        //将日志记录到缓存
        redisTemplate.opsForList().rightPush(RedisKeyConfig.TEMP_VISITOR_LOG, visitLog);
    }

    @Override
    public int getVisitorCount(long startTime, long end) {
        return visitLogMapper.getVisitorCount(startTime, end);
    }

    @Override
    public List<VOVisitorCityInfo> getVisitorCityInfo() {
        return visitLogMapper.getVisitorCityInfo();
    }

    @Override
    public int getNewVisitorCount(long lastDate) {
        return visitLogMapper.getVisitorCount(lastDate, System.currentTimeMillis());
    }

    @Override
    public int getUniqueIpCount() {
        return visitLogMapper.getUniqueIpCount();
    }

    @Override
    public int getNewUniqueIpCount(long lastDate) {
        return visitLogMapper.getNewUniqueIpCount(lastDate);
    }

    @Override
    public Map<String, List<Object>> getVisitorCountGroupByDays(long start, long end) {
        LinkedList<String> days = new LinkedList<>();
        for (long i = start; i < end; i += 1000 * 60 * 60 * 24) {
            days.add(DateUtils.format("M/dd", new Date(i)));
        }
        List<Map<String, Object>> visitorCountGroupByDays = visitLogMapper.getVisitorCountGroupByDays(start, end, days);
        HashMap<String, List<Object>> res = new HashMap<>();
        LinkedList<Object> dates = new LinkedList<>();
        LinkedList<Object> values = new LinkedList<>();
        res.put("dates", dates);
        res.put("values", values);
        for (Map<String, Object> item : visitorCountGroupByDays) {
            dates.add(item.get("days"));
            values.add(item.get("count"));
        }
        return res;
    }
}
